﻿namespace ImageProcessing.Forms
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;

    /// <summary>
    /// Fourier effects form
    /// </summary>
    public partial class FourierEffectsForm : Form
    {
        /// <summary>
        /// Fourier image
        /// </summary>
        private FourierImage fourierImage;

        /// <summary>
        /// OriginalImage
        /// </summary>
        private Bitmap OriginalImage;

        /// <summary>
        /// Image mask
        /// </summary>
        private double[,] mask;

        /// <summary>
        /// Mask image
        /// </summary>
        private ImageStructure[,] maskImage;

        /// <summary>
        /// Image width
        /// </summary>
        private int width;

        /// <summary>
        /// Band type for notch and band filters
        /// </summary>
        private BandType bandType;

        /// <summary>
        /// Image height
        /// </summary>
        private int height;

        /// <summary>
        /// Corner point of picture in the picture box
        /// </summary>
        private Point corner;

        /// <summary>
        /// Radius or %
        /// </summary>
        private bool radius;

        /// <summary>
        /// Post processing type.
        /// </summary>
        private PostProcessing postProcessing;

        /// <summary>
        /// Displayed image
        /// </summary>
        private ImageStructure[,] displayedImage;

        /// <summary>
        /// Initializes a new instance of the <see cref="FourierEffectsForm"/> class.
        /// </summary>
        /// <param name="img">The img.</param>
        public FourierEffectsForm(FourierImage img)
        {
            this.InitializeComponent();

            this.fourierImage = img;
            this.height = img.U;
            this.width = img.V;

            this.OriginalImage = img.Image.ImageBitmap;
            this.mask = new double[this.height, this.width];

            this.pcbMain.Image = Operations.DisplayMatrix(img.Image.ImageStructArray);
            this.CmbFilterType.SelectedIndex = 0;
            this.Cbx_DisImage.SelectedIndex = 0;

            this.TkbCircleRadius.Maximum = Math.Max(this.height, this.width);

            this.SetCorner();
            this.LblH.Text = "H: " + this.height;
            this.LblW.Text = "W: " + this.width;
            this.LblX.Text = "X:";
            this.LblY.Text = "Y:";
            this.LblName.Text = "Name: " + img.Image.Name;
            this.LblPath.Text = "Path: " + img.Image.FilePath;
        }

        /// <summary>
        /// Handles the Scroll event of the TkbCircleRadius control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void TkbCircleRadius_Scroll(object sender, EventArgs e)
        {
            int circleRadius = 0;
            if (!this.radius)
            {
                circleRadius = (this.TkbCircleRadiusPerc.Value * Math.Max(this.height, this.width)) / 100;
                if (circleRadius > this.TkbCircleRadius.Maximum)
                {
                    this.TkbCircleRadius.Maximum = circleRadius;
                }

                this.TkbCircleRadius.Value = circleRadius;
            }
            else
            {
                circleRadius = this.TkbCircleRadius.Value;
                try
                {
                    this.TkbCircleRadiusPerc.Value = (int)((double)circleRadius / Math.Max(this.height, this.width) * 100.0);
                }
                catch
                {
                    this.TkbCircleRadiusPerc.Value = 1;
                }
                this.radius = false;
            }

            int notchX = 0, notchY = 0;
            if (int.TryParse(this.txtX0.Text, out notchX))
            {
                if (!int.TryParse(this.txtY0.Text, out notchY) && this.CmbFilterType.SelectedIndex != 3)
                {
                    MessageBox.Show("Please enter correct integer values.");
                    return;
                }
            }

            if (this.rdbGaussian.Checked)
            {
                if (this.TkbCircleRadiusPerc.Value < 100)
                {
                    if (this.CmbFilterType.SelectedIndex == 2)
                    {
                        this.maskImage = SignalNoiseFilters.NotchFilter(this.fourierImage.Image.ImageStructArray, circleRadius, new Point(notchX, notchY), NoiseFilterType.Gaussian, ref this.mask, this.TkbButterWorthN.Value, this.bandType);
                    }
                    else if (this.CmbFilterType.SelectedIndex == 3)
                    {
                        this.maskImage = SignalNoiseFilters.BandFilter(this.fourierImage.Image.ImageStructArray, circleRadius, notchX, NoiseFilterType.Gaussian, ref this.mask, this.TkbButterWorthN.Value, this.bandType);
                    }
                    else
                    {
                        this.maskImage = FourierEffects.DrawGaussianCircle(this.fourierImage.Image.ImageStructArray, circleRadius / 2, out this.mask);
                    }
                }
            }
            else if (this.rdbButterworth.Checked)
            {
                if (this.TkbCircleRadiusPerc.Value < 100)
                {
                    if (this.CmbFilterType.SelectedIndex == 2)
                    {
                        this.maskImage = SignalNoiseFilters.NotchFilter(this.fourierImage.Image.ImageStructArray, circleRadius, new Point(notchX, notchY), NoiseFilterType.Butterworth, ref this.mask, this.TkbButterWorthN.Value, this.bandType);
                    }
                    else if (this.CmbFilterType.SelectedIndex == 3)
                    {
                        this.maskImage = SignalNoiseFilters.BandFilter(this.fourierImage.Image.ImageStructArray, circleRadius, notchX, NoiseFilterType.Butterworth, ref this.mask, this.TkbButterWorthN.Value, this.bandType);
                    }
                    else
                    {
                        this.maskImage = FourierEffects.DrawButterWorthCircle(this.fourierImage.Image.ImageStructArray, circleRadius / 2, this.TkbButterWorthN.Value, out this.mask);
                    }
                }
            }
            else if (this.rdbIdeal.Checked)
            {
                if (this.TkbCircleRadiusPerc.Value < 100)
                {
                    if (this.CmbFilterType.SelectedIndex == 2)
                    {
                        this.maskImage = SignalNoiseFilters.NotchFilter(this.fourierImage.Image.ImageStructArray, circleRadius, new Point(notchX, notchY), NoiseFilterType.Ideal, ref this.mask, this.TkbButterWorthN.Value, this.bandType);
                    }
                    else if (this.CmbFilterType.SelectedIndex == 3)
                    {
                        this.maskImage = SignalNoiseFilters.BandFilter(this.fourierImage.Image.ImageStructArray, circleRadius, notchX, NoiseFilterType.Ideal, ref this.mask, this.TkbButterWorthN.Value, this.bandType);
                    }
                    else
                    {
                        this.maskImage = FourierEffects.DrawIdealCircle(this.fourierImage.Image.ImageStructArray, circleRadius / 2, out this.mask);
                    }
                }
            }

            if (this.CmbFilterType.SelectedIndex == 1)
            {
                this.mask = FourierEffects.InverseMask(this.mask, ref this.maskImage);
            }

            this.TxtCircleRadiusPer.Text = this.TkbCircleRadiusPerc.Value.ToString();
            this.TxtButterWorthN.Text = this.TkbButterWorthN.Value.ToString();
            this.TxtCircleRadius.Text = this.TkbCircleRadius.Value.ToString();
            this.UpdateDisplayedImage();
        }

        /// <summary>
        /// Handles the CheckedChanged event of the rdbIdeal control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void RdbIdeal_CheckedChanged(object sender, EventArgs e)
        {
            int circleRadius = (this.TkbCircleRadiusPerc.Value * Math.Max(this.height, this.width)) / 100;

            // int circleRadius = this.TkbCircleRadius.Value;
            if (this.rdbGaussian.Checked)
            {
                if (this.TkbCircleRadiusPerc.Value < 100)
                {
                    this.maskImage = FourierEffects.DrawGaussianCircle(this.fourierImage.Image.ImageStructArray, circleRadius / 2, out this.mask);
                }

                this.TkbButterWorthN.Enabled = false;
            }
            else if (this.rdbButterworth.Checked)
            {
                if (this.TkbCircleRadiusPerc.Value < 100)
                {
                    this.maskImage = FourierEffects.DrawButterWorthCircle(this.fourierImage.Image.ImageStructArray, circleRadius / 2, this.TkbButterWorthN.Value, out this.mask);
                }

                this.TkbButterWorthN.Enabled = true;
            }
            else if (this.rdbIdeal.Checked)
            {
                if (this.TkbCircleRadiusPerc.Value < 100)
                {
                    this.maskImage = FourierEffects.DrawIdealCircle(this.fourierImage.Image.ImageStructArray, circleRadius / 2, out this.mask);
                }
                else
                {
                    this.maskImage = FourierEffects.ReturnWhiteImage(this.fourierImage.Image.ImageStructArray, out this.mask);
                }

                this.TkbButterWorthN.Enabled = false;
            }

            if (this.CmbFilterType.SelectedIndex == 1)
            {
                this.mask = FourierEffects.InverseMask(this.mask, ref this.maskImage);
            }

            TkbCircleRadius_Scroll(sender, e);
            this.UpdateDisplayedImage();
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the Cbx_DisImage control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Cbx_DisImage_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.UpdateDisplayedImage();
        }

        /// <summary>
        /// Updates the displayed image.
        /// </summary>
        private void UpdateDisplayedImage()
        {
            if (this.mask == null)
            {
                return;
            }

            if (this.Cbx_DisImage.SelectedIndex == 0)
            {
                //this.pcbMain.Image = Operations.DisplayMatrix(this.fourierImage.ApplyMaskOnSpatial(this.mask));
                this.pcbMain.Image = OriginalImage;
            }
            else if (this.Cbx_DisImage.SelectedIndex == 1)
            {
                this.pcbMain.Image = Operations.DisplayMatrix(this.maskImage);
            }
            else if (this.Cbx_DisImage.SelectedIndex == 2)
            {
                this.pcbMain.Image = Operations.DisplayMatrix(this.fourierImage.ApplyMaskOnMagnitude(this.mask));
            }
            else if (this.Cbx_DisImage.SelectedIndex == 3)
            {
                this.displayedImage = this.fourierImage.ApplyMaskOnFreq(this.mask, this.postProcessing);
                this.pcbMain.Image = Operations.DisplayMatrix(this.displayedImage);
            }
        }

        /// <summary>
        /// Sets the corner.
        /// </summary>
        private void SetCorner()
        {
            int x = (this.pcbMain.Width - this.width) / 2;
            int y = (this.pcbMain.Height - this.height) / 2;

            this.corner = new Point(x, y);
        }

        /// <summary>
        /// Handles the MouseMove event of the pcbMain control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void PcbMain_MouseMove(object sender, MouseEventArgs e)
        {
            if ((e.X > (this.corner.X + this.width) || e.X < this.corner.X) || (e.Y > (this.corner.Y + this.height) || e.Y < this.corner.Y))
            {
                this.LblX.Text = "X: ";
                this.LblY.Text = "Y: ";
            }
            else
            {
                int x, y;
                x = (e.X - this.corner.X) + 1;
                y = (this.corner.Y - e.Y) - 1;

                x += -1 * (this.width / 2);
                y += this.height / 2;

                this.LblX.Text = "X: " + x.ToString();
                this.LblY.Text = "Y: " + y.ToString();
            }
        }

        /// <summary>
        /// Handles the 1 event of the TkbCircleRadius_Scroll control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void TkbCircleRadius_Scroll_1(object sender, EventArgs e)
        {
            this.radius = true;
            this.TkbCircleRadius_Scroll(sender, e);
        }

        /// <summary>
        /// Handles the CheckedChanged event of the RdbNormalization control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void RdbNormalization_CheckedChanged(object sender, EventArgs e)
        {
            if (this.RdbCutoff.Checked && this.RdbCutoff.Text != "Reject")
            {
                this.postProcessing = PostProcessing.CuttOff;
            }
            else if (this.RdbNormalization.Checked && this.RdbNormalization.Text != "Pass")
            {
                this.postProcessing = PostProcessing.Normalization;
            }
            else if (this.RdbCutoff.Checked && this.RdbCutoff.Text == "Reject")
            {
                this.bandType = BandType.Reject;
            }
            else if (this.RdbNormalization.Checked && this.RdbNormalization.Text == "Pass")
            {
                this.bandType = BandType.Pass;
            }
            TkbCircleRadius_Scroll(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the BtnCancel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void BtnCancel_Click(object sender, EventArgs e)
        {
            //this.fourierImage.GetSpatial();
            //this.fourierImage.Image.UpdateImage(OriginalImage);
            this.Close();
        }

        /// <summary>
        /// Handles the Click event of the BtnOk control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void BtnOk_Click(object sender, EventArgs e)
        {
            this.fourierImage.Image.BackupImageArray();
            if (this.CmbFilterType.SelectedIndex == 0)
            {
                if (this.rdbButterworth.Checked)
                {
                    if (this.postProcessing == PostProcessing.CuttOff)
                    {
                        Main.OperationName = "Low pass. Butterworth. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing cutoff";
                    }
                    else if (this.postProcessing == PostProcessing.Normalization)
                    {
                        Main.OperationName = "Low pass. Butterworth. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing normalization";
                    }
                }
                else if (this.rdbGaussian.Checked)
                {
                    if (this.postProcessing == PostProcessing.CuttOff)
                    {
                        Main.OperationName = "Low pass. Gaussian. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing cutoff";
                    }
                    else if (this.postProcessing == PostProcessing.Normalization)
                    {
                        Main.OperationName = "Low pass. Gaussian. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing normalization";
                    }
                }
                else if (this.rdbIdeal.Checked)
                {
                    if (this.postProcessing == PostProcessing.CuttOff)
                    {
                        Main.OperationName = "Low pass. Ideal. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing cutoff";
                    }
                    else if (this.postProcessing == PostProcessing.Normalization)
                    {
                        Main.OperationName = "Low pass. Ideal. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing normalization";
                    }
                }
            }
            else if (this.CmbFilterType.SelectedIndex == 1)
            {
                if (this.rdbButterworth.Checked)
                {
                    if (this.postProcessing == PostProcessing.CuttOff)
                    {
                        Main.OperationName = "High pass. Butterworth. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing cutoff";
                    }
                    else if (this.postProcessing == PostProcessing.Normalization)
                    {
                        Main.OperationName = "High pass. Butterworth. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing normalization";
                    }
                }
                else if (this.rdbGaussian.Checked)
                {
                    if (this.postProcessing == PostProcessing.CuttOff)
                    {
                        Main.OperationName = "High pass. Gaussian. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing cutoff";
                    }
                    else if (this.postProcessing == PostProcessing.Normalization)
                    {
                        Main.OperationName = "High pass. Gaussian. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing normalization";
                    }
                }
                else if (this.rdbIdeal.Checked)
                {
                    if (this.postProcessing == PostProcessing.CuttOff)
                    {
                        Main.OperationName = "High pass. Ideal. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing cutoff";
                    }
                    else if (this.postProcessing == PostProcessing.Normalization)
                    {
                        Main.OperationName = "High pass. Ideal. R: " + this.TkbCircleRadius.Value.ToString() + " .Post processing normalization";
                    }
                }
            }

            this.fourierImage.Image.UpdateImage(this.displayedImage);
            this.Close();
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the CmbFilterType control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void CmbFilterType_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.CmbFilterType.SelectedIndex == 2 || this.CmbFilterType.SelectedIndex == 3)
            {
                this.grbPostProcessing.Text = "Band type";
                this.RdbNormalization.Text = "Pass";
                this.RdbCutoff.Text = "Reject";
            }
            else
            {
                this.grbPostProcessing.Text = "Post Processing";
                this.RdbNormalization.Text = "Normalization";
                this.RdbCutoff.Text = "Cutoff";
            }

            if (this.CmbFilterType.SelectedIndex == 2)
            {
                this.txtX0.Enabled = this.txtY0.Enabled = true;
                this.lblX0.Text = "X0";
            }
            else if (this.CmbFilterType.SelectedIndex == 3)
            {
                this.txtX0.Enabled = true;
                this.txtY0.Enabled = false;
                this.lblX0.Text = "Band W";
                this.TKB_Thickness.Enabled = true;
                this.Txt_Thickness.Enabled = true;
            }
            else
            {
                this.txtX0.Enabled = this.txtY0.Enabled = false;
                this.TKB_Thickness.Enabled = false;
                this.Txt_Thickness.Enabled = false;
            }

            this.RdbNormalization_CheckedChanged(sender, e);
            TkbCircleRadius_Scroll(sender, e);
        }

        /// <summary>
        /// Handles the MouseClick event of the pcbMain control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void PcbMain_MouseClick(object sender, MouseEventArgs e)
        {
            int x = 0, y = 0;

            if ((e.X > (this.corner.X + this.width) || e.X < this.corner.X) || (e.Y > (this.corner.Y + this.height) || e.Y < this.corner.Y))
            {
                this.LblX.Text = "X: ";
                this.LblY.Text = "Y: ";
            }
            else
            {
                x = (e.X - this.corner.X) + 1;
                y = (this.corner.Y - e.Y) - 1;

                x += -1 * (this.width / 2);
                y += this.height / 2;
            }

            if (!(this.LblY.Text == "Y: " && this.LblX.Text == "X: "))
            {
                x *= -1;

                this.txtX0.Text = x.ToString();
                this.txtY0.Text = y.ToString();
            }
            TkbCircleRadius_Scroll(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the BtnGo control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void BtnGo_Click(object sender, EventArgs e)
        {
            this.fourierImage.Image.UpdateImage(this.fourierImage.ApplyMaskOnFreq(this.mask, this.postProcessing));
            this.fourierImage.InitAndReturn();
            this.UpdateDisplayedImage();
        }

        private void Txt_Thickness_TextChanged(object sender, EventArgs e)
        {
            try
            {
                this.TKB_Thickness.Value = int.Parse(Txt_Thickness.Text);
                this.txtX0.Text = Txt_Thickness.Text;
                TkbCircleRadius_Scroll(sender, e);
            }
            catch (Exception ex)
            {
                Logger.LogException(ex);
                MessageBox.Show("Thickness must be positive integer value");
            }
        }

        private void TKB_Thickness_Scroll(object sender, EventArgs e)
        {
            this.Txt_Thickness.Text = TKB_Thickness.Value.ToString();
            //  this.txtX0.Text = TKB_Thickness.Value.ToString();
            //  TkbCircleRadius_Scroll(sender, e);

        }
    }
}